﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SecureIrcConnection.cs" company="LeetSoftwerx">
//   Copyright ©2012 LeetSoftwerx
// </copyright>
// <summary>
//   Implements a secure connection to an IRC server
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Irclib
{
    using System;
    using System.Diagnostics.Contracts;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;

    /// <summary>
    /// Implements a secure connection to an IRC server
    /// </summary>
    public sealed class SecureIrcConnection : IrcConnection, IDisposable
    {
        #region Constants and Fields

        /// <summary>
        /// The accept invalid cert.
        /// </summary>
        private readonly bool acceptInvalidCert;

        /// <summary>
        /// The secure stream.
        /// </summary>
        private SslStream secureStream;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SecureIrcConnection"/> class. 
        /// Creates a new instance of a secure connection to an IRC server
        /// </summary>
        /// <param name="server">
        /// The URI of the server to connect to 
        /// </param>
        /// <param name="serverEncoding">
        /// The Encoding of the server 
        /// </param>
        /// <param name="acceptInvalidCert">
        /// Whether or not to accept an invalid certificate 
        /// </param>
        /// <param name="userName">
        /// The username to send on connect 
        /// </param>
        public SecureIrcConnection(Uri server, Encoding serverEncoding, bool acceptInvalidCert, string userName)
            : base(server, serverEncoding, userName)
        {
            this.acceptInvalidCert = acceptInvalidCert;
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Connects and authenticates to the server
        /// </summary>
        public override void Connect()
        {
            base.Connect();
            this.secureStream = new SslStream(
                this.Client.GetStream(), true, this.ValidateServerCertificate);
            this.secureStream.AuthenticateAsClient(this.Server.DnsSafeHost);
        }

        /// <summary>
        /// Initializes the connection for use, MUST be called after Connect()
        /// </summary>
        public override void Initialize()
        {
            Initialize(this.secureStream);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Disposes of the resources held by this connection
        /// </summary>
        /// <param name="disposing">
        /// Whether or not to release the unmanaged resources 
        /// </param>
        protected override void Dispose(bool disposing)
        {
            Contract.Assume(!disposing || this.secureStream != null);
            if (disposing)
            {
                this.secureStream.Dispose();
            }

            base.Dispose(disposing);
        }

        /// <summary>
        /// The validate server certificate.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="certificate">
        /// The certificate.
        /// </param>
        /// <param name="chain">
        /// The chain.
        /// </param>
        /// <param name="sslPolicyErrors">
        /// The ssl policy errors.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if the user has selected to ignore invalid certificates
        /// </returns>
        private bool ValidateServerCertificate(
            object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return sslPolicyErrors == SslPolicyErrors.None || this.acceptInvalidCert;
        }

        #endregion
    }
}